﻿using System;
using System.Configuration;
using System.Xml;

namespace UDS.Configuration
{
    public class NameValueSectionHandler : IConfigurationSectionHandler
    {
        #region IConfigurationSectionHandler Members

        /// <summary>
        /// Creates a new configuration handler and adds it to the section-handler collection based on the specified parameters.
        /// </summary>
        public object Create(object parent, object configContext, XmlNode section)
        {
            return CreateStatic(parent, section, "key", "value");
        }

        #endregion

        internal static object CreateStatic(object parent, XmlNode section, string keyAttriuteName, string valueAttributeName)
        {
            // Read configuration settings in the parent config files.
            ReadOnlyNameValueCollection values;
            if (parent == null)
            {
                values = new ReadOnlyNameValueCollection(StringComparer.OrdinalIgnoreCase);
            }
            else
            {
                var parentValues = (ReadOnlyNameValueCollection)parent;
                values = new ReadOnlyNameValueCollection(parentValues);
            }

            if (section.Attributes.Count != 0)
            {
                throw new ConfigurationErrorsException(string.Format("Unrecognized attribute: {0}.", section.Attributes[0].Name), section);
            }
            foreach (XmlNode node in section.ChildNodes)
            {
                if (node.NodeType == XmlNodeType.Comment || node.NodeType == XmlNodeType.Whitespace)
                {
                    continue;
                }
                if (node.NodeType != XmlNodeType.Element)
                {
                    throw new ConfigurationErrorsException("Unknown config elements.", section);
                }

                if (node.Name == "add")
                {
                    string key = RemoveRequiredAttribute(node, keyAttriuteName, false);
                    string value = RemoveRequiredAttribute(node, valueAttributeName, true);
                    CheckForUnrecognizedAttributes(node);

                    values[key] = Utility.ResolvePath(value);
                    continue;
                }

                if (node.Name == "remove")
                {
                    string key = RemoveRequiredAttribute(node, keyAttriuteName, false);
                    CheckForUnrecognizedAttributes(node);
                    values.Remove(key);
                    continue;
                }

                if (node.Name.Equals("clear"))
                {
                    CheckForUnrecognizedAttributes(node);
                    values.Clear();
                    continue;
                }

                throw new ConfigurationErrorsException(string.Format("Unrecognized element."), node);
            }
            values.SetReadOnly();
            return values;
        }

        private static string RemoveRequiredAttribute(XmlNode node, string name, bool allowEmpty)
        {
            XmlNode attNode = node.Attributes.RemoveNamedItem(name);
            if (attNode == null)
            {
                throw new ConfigurationErrorsException(string.Format("The attribute '{0}' is required.", name), node);
            }
            if (string.IsNullOrEmpty(attNode.Value) && !allowEmpty)
            {
                throw new ConfigurationErrorsException(string.Format("The attribute '{0}' cannot be empty.", name), node);
            }
            return attNode.Value;
        }

        private static void CheckForUnrecognizedAttributes(XmlNode node)
        {
            if (node.Attributes.Count != 0)
            {
                throw new ConfigurationErrorsException(string.Format("Unrecognized attribute '{0}'.", node.Attributes[0].Name), node);
            }
        }
    }
}